#define _GNU_SOURCE
#include "app_serial.h"
#include "log/log.h"

/**
 * @brief 设置串口的波特率
 *
 * @param dev 串口文件描述符
 * @param baudRate 波特率
 * @return int 成功返回0，失败返回-1
 */
int app_serial_setBaudRate(Device_t *dev, BaudRate_t baudRate)
{
    // 读取串口属性
    struct termios attr;
    tcgetattr(dev->fd, &attr);

    // 修改属性
    // cfsetispeed(&attr, baudRate);
    // cfsetospeed(&attr, baudRate);
    cfsetspeed(&attr, baudRate);

    // 设置串口属性
    int result = tcsetattr(dev->fd, TCSAFLUSH, &attr);
    if (result != 0)
    {
        log_error("设置串口波特率失败");
        return -1;
    }
    log_debug("设置串口波特率成功");
    return 0;
}

/**
 * @brief 设置串口的校验位
 *
 * @param dev 串口文件描述符
 * @param parity 校验位
 * @return int 成功返回0，失败返回-1
 */
int app_serial_setParity(Device_t *dev, Parity_t parity)
{
    // 读取串口属性
    struct termios attr;
    tcgetattr(dev->fd, &attr);

    // 修改属性
    attr.c_cflag &= ~(PARENB | PARODD); // 清除校验位
    attr.c_cflag |= parity;             // 设置校验位

    // 设置串口属性
    int result = tcsetattr(dev->fd, TCSAFLUSH, &attr);
    if (result != 0)
    {
        log_error("设置串口校验位失败");
        return -1;
    }
    log_debug("设置串口校验位成功");
    return 0;
}

/**
 * @brief 设置串口的停止位
 *
 * @param dev 串口文件描述符
 * @param stopBit 停止位
 * @return int 成功返回0，失败返回-1
 */
int app_serial_setStopBit(Device_t *dev, StopBit_t stopBit)
{
    // 读取串口属性
    struct termios attr;
    tcgetattr(dev->fd, &attr);

    // 修改属性
    attr.c_cflag &= ~CSTOPB; // 清除停止位
    attr.c_cflag |= stopBit; // 设置停止位

    // 设置串口属性
    int result = tcsetattr(dev->fd, TCSAFLUSH, &attr);
    if (result != 0)
    {
        log_error("设置串口停止位失败");
        return -1;
    }
    log_debug("设置串口停止位成功");
    return 0;
}

/**
 * @brief 设置串口的阻塞模式
 *
 * @param dev 串口文件描述符
 * @param block 阻塞模式
 * @return int 成功返回0，失败返回-1
 */
int app_serial_setBlock(Device_t *dev, int is_block)
{
    // 读取串口属性
    struct termios attr;
    tcgetattr(dev->fd, &attr);

    // 修改属性
    if (is_block)
    {                         // 阻塞模式
        attr.c_cc[VMIN] = 1;  // 设置最小接收字符数
        attr.c_cc[VTIME] = 0; // 设置超时时间为0
    }
    else
    {                         // 非阻塞模式
        attr.c_cc[VMIN] = 0;  // 设置最小接收字符数
        attr.c_cc[VTIME] = 2; // 设置超时时间为200ms,单位：0.1s
    }

    // 设置串口属性
    int result = tcsetattr(dev->fd, TCSAFLUSH, &attr);
    if (result != 0)
    {
        log_error("设置串口阻塞模式失败");
        return -1;
    }
    log_debug("设置串口阻塞模式成功");
    return 0;
}

/**
 * @brief 设置串口为原始模式
 *
 * @param dev 串口文件描述符
 * @return int 成功返回0，失败返回-1
 */
int app_serial_setRaw(Device_t *dev)
{
    // 读取串口属性
    struct termios attr;
    tcgetattr(dev->fd, &attr);

    // 修改属性
    cfmakeraw(&attr);

    // 设置串口属性
    int result = tcsetattr(dev->fd, TCSAFLUSH, &attr);
    if (result != 0)
    {
        log_error("设置串口波特率失败");
        return -1;
    }
    log_debug("设置串口波特率成功");
    return 0;
}

/**
 * @brief 刷新串口缓冲区
 * 
 * @param dev 串口文件描述符
 * @return int 成功返回0，失败返回-1
 */
int app_serial_flush(Device_t *dev)
{
    return tcflush(dev->fd, TCIOFLUSH);
}

/**
 * @brief 初始化串口
 *
 * @param dev 串口文件描述符
 * @return int 成功返回0，失败返回-1
 */
int app_serial_init(Device_t *dev)
{
    // 配置各个属性
    // 设置波特率
    if (app_serial_setBaudRate(dev, BR_9600) != 0)
    {
        return -1;
    }
    // 设置校验位
    if (app_serial_setParity(dev, PARITY_NONE) != 0)
    {
        return -1;
    }
    // 设置停止位
    if (app_serial_setStopBit(dev, SB_1) != 0)
    {
        return -1;
    }
    // 设置阻塞模式==>这里设置非阻塞模式无效
    // if (app_serial_setBlock(dev, 0) != 0)
    // {
    //     log_debug("设置串口阻塞模式失败");
    //     return -1;
    // }
    // 设置为原始模式
    if (app_serial_setRaw(dev) != 0)
    {
        return -1;
    }
    // 刷新串口
    if (app_serial_flush(dev) != 0)
    {
        log_debug("刷新串口缓冲区失败");
        return -1;
    }
    log_debug("初始化串口成功");
    return 0;
}